﻿using ValidationEngine.Commons;
using ValidationEngine.Commons.Enums;
using ValidationEngine.Commons.Interfaces;

namespace ValidationEngine.ValidationRules
{
    /// <summary>
    /// Base class for all validation rules. Provides some basic and common functionality
    /// </summary>
    public abstract class BaseValidationRule : IValidationRule
    {
        #region Members
        /// <summary>
        /// Stores validation rule result after applying
        /// </summary>
        protected IValidationInfo _result = new ValidationInfo();
        /// <summary>
        /// True if some error ocurred during rule applying
        /// </summary>
        protected bool _brokenRule = false;
        /// <summary>
        /// Contains reason if _brokenRule is true
        /// </summary>
        protected string _brokenReason = string.Empty;
        /// <summary>
        /// Default error message for the rule
        /// </summary>
        protected string _defErrorMsg;
        /// <summary>
        /// Default warning message for the rule
        /// </summary>
        protected string _defWarningMsg;
        /// <summary>
        /// Default information message for the rule
        /// </summary>
        protected string _defInfoMsg;
        /// <summary>
        /// Default error type for the rule
        /// </summary>
        protected ValidationErrorType _defErrorType; 
        #endregion

        #region IValidationRule

        /// <summary>
        /// Executes rule logic on model's property from context and returns results
        /// </summary>
        /// <param name="ctx">Validation context</param>
        /// <returns>Results of rule execution.</returns>
        public IValidationInfo Apply(IValidationContext ctx)
        {
            Result = new ValidationInfo();
            // check if rule can be applied
            if (null == ctx.Model || !ShouldApplyForContext(ctx.ApplingContext))
            {
                return Result;
            }
            return ApplyRule(ctx);
        } 

        #endregion

        #region Tools
        /// <summary>
        /// Checks if the rule can be applyed for context.
        /// </summary>
        /// <param name="ruleApplingContext">Rule appling context.</param>
        /// <returns>True if can be applied.</returns>
        private bool ShouldApplyForContext(RuleApplingContext ruleApplingContext)
        {
            if (this.ApplingContext == RuleApplingContext.Never)
            {
                return false;
            }
            else if (this.ApplingContext == RuleApplingContext.Always)
            {
                return true;
            }
            else if (this.ApplingContext == ruleApplingContext)
            {
                return true;
            }
            return false;
        } 
        #endregion

        #region Abstract methods
        /// <summary>
        /// Applies rule logic on model from context and return result
        /// </summary>
        /// <param name="ctx">Validation context</param>
        /// <returns>Validation info</returns>
        protected abstract IValidationInfo ApplyRule(IValidationContext ctx); 
        #endregion

        #region Properties
        /// <summary>
        /// Describes when rule should be applied
        /// </summary>
        public RuleApplingContext ApplingContext { get; set; }

        /// <summary>
        /// Stores result of rule appling
        /// </summary>
        protected IValidationInfo Result
        {
            get { return _result; }
            set { _result = value; }
        }

        /// <summary>
        /// Provides quick result setting for rule default error type
        /// </summary>
        protected string DefResult
        {
            set
            {
                switch (_defErrorType)
                {
                    case ValidationErrorType.Error:
                        _result.SetError(value);
                        break;
                    case ValidationErrorType.Warning:
                        _result.SetWarning(value);
                        break;
                    case ValidationErrorType.Information:
                        _result.SetInfo(value);
                        break;
                    default:
                        break;
                }
            }
        } 
        #endregion
    }
}
